#pragma once 
#include <Windows.h>
#include <string>
#include <map>
using namespace std;

class DllParser
{
public:

	DllParser()
	{}

	~DllParser()
	{
		UnLoad();
	}

	virtual bool Load( LPCTSTR dllPath )
	{
		m_hMod = LoadLibrary( dllPath );
		if ( nullptr == m_hMod )
		{
			//printf( "LoadLibrary failed\n" );
			return false;
		}

		return true;
	}

	bool UnLoad()
	{
		if ( m_hMod == nullptr )
			return false;

		auto b = FreeLibrary( m_hMod );
		if ( !b )
			return false;

		m_hMod = nullptr;
		return true;
	}


	template <typename T>
	std::function<T> GetFunction( const string& funcName )
	{
		auto it = m_map.find( funcName );
		if ( it == m_map.end() )
		{
			auto addr = GetProcAddress( m_hMod, funcName.c_str() );
			m_map.insert( std::make_pair( funcName, addr ) );
			it = m_map.find( funcName );
		}

		return std::function<T>( (T*) (it->second) );
	}

	template <typename T, typename... Args>
	typename std::result_of<std::function<T>( Args... )>::type ExcecuteFunc( const string& funcName, Args&&... args )
	{
		return GetFunction<T>( funcName )(args...);
	}

protected:
	HMODULE m_hMod;
	std::map<string, FARPROC> m_map;
};

class ComLoader
	: public DllParser
{
public:
	ComLoader() {}

	bool Load( LPCTSTR dllPath )
	{
		m_hMod = LoadLibrary( dllPath );
		if ( nullptr == m_hMod )
		{
			//printf( "LoadLibrary failed\n" );
			return false;
		}
		return true;
	}
	IDispatch* GetDispatch( CLSID clsid )
	{
		IClassFactory* pClassFactory;
		
		HRESULT hr = ExcecuteFunc<HRESULT __stdcall(REFCLSID, REFIID, void**)>( "DllGetClassObject", clsid, IID_IClassFactory, (void**) &pClassFactory );
		if ( SUCCEEDED( hr ) && pClassFactory )
		{
			IDispatch* disp = nullptr;
			hr = pClassFactory->CreateInstance( NULL, IID_IDispatch, (void**) &disp );

			if ( SUCCEEDED( hr ) )
			{
				return disp;
			}
		}
		return nullptr;
	}
	template<typename Interface>
	Interface* GetInterface( CLSID clsid, IID iid )
	{
		IClassFactory* pClassFactory;

		HRESULT hr = ExcecuteFunc<HRESULT __stdcall(REFCLSID, REFIID, void**)>( "DllGetClassObject", clsid, IID_IClassFactory, (void**) &pClassFactory );
		if ( SUCCEEDED( hr ) && pClassFactory )
		{
			Interface* disp = nullptr;
			hr = pClassFactory->CreateInstance( NULL, iid, (void**) &disp );

			if ( SUCCEEDED( hr ) )
			{
				return disp;
			}
		}
		return nullptr;
	}
	private:
};